package com.ybear.ybutils.utils;

import android.text.TextUtils;

import com.ybear.ybutils.utils.log.LogUtil;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ClassUtils {
    /**
     * 获取方法（不限于public, protected, private）
     * @param obj               当前调用的对象/类
     * @param methodName        方法名
     * @return                  {@link Method}
     */
    public static Method getMethod(Object obj, String methodName, Class<?>... parameterTypes) {
        if( obj == null || TextUtils.isEmpty( methodName ) ) return null;
        Class<?> cls = obj instanceof Class<?> ? (Class<?>) obj : obj.getClass();
        try {
            return parameterTypes == null ?
                    cls.getMethod( methodName ) :
                    cls.getMethod( methodName, parameterTypes );
        }catch(NoSuchMethodException e) {
            try {
                return cls.getDeclaredMethod( methodName );
            }catch(NoSuchMethodException e1) {
                if( ( cls = cls.getSuperclass() ) == null ) {
                    LogUtil.e( "UtilsTAG", "Method [%s] not found.", methodName );
                    return null;
                }
                return getMethod( cls, methodName );
            }
        }
    }

    /**
     * 调用方法（不限于public, protected, private）
     * @param obj               当前调用的对象/类
     * @param methodName        方法名
     * @param args              方法参数。eg: new Object[] { 1, "test" }
     * @param argsType          参数类型。eg: Integer.class, String.class
     * @return                  调用方法返回的数据
     */
    public static Object invokeMethod(Object obj, Object invoke, String methodName,
                                      Object[] args, Class<?>[] argsType,
                                      Class<?>... parameterTypes) {

        if( obj == null || methodName == null || "".equals( methodName ) ) return null;
        Method method = getMethod( obj, methodName, parameterTypes );
        if( method == null ) return null;
        method.setAccessible( true );
        try {
            if( args == null || args.length == 0 || argsType == null || args.length != argsType.length ) {
                try {
                    return method.invoke( invoke );
                } catch (Exception e) {
                    return method.invoke( null );
                }
            }
            try {
                return method.invoke( invoke, args );
            } catch (Exception e) {
                return method.invoke( null, args );
            }
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Object invokeMethod(Object obj, String methodName,
                                      Object[] args, Class<?>[] argsType,
                                      Class<?>... parameterTypes) {
        return invokeMethod( obj, obj, methodName, args, argsType, parameterTypes );
    }

    /**
     * 调用方法（不限于public, protected, private）
     * @param obj               当前调用的对象/类
     * @param methodName        方法名
     * @return                  调用方法返回的数据
     */
    public static Object invokeMethod(Object obj, String methodName,
                                      Class<?>... parameterTypes) {
        return invokeMethod( obj, methodName, null, null, parameterTypes );
    }

    /**
     * 调用方法（不限于public, protected, private）
     * @param obj               当前调用的对象/类
     * @param methodName        方法名
     * @return                  调用方法返回的数据
     */
    public static Object invokeMethod(Object obj, String methodName) {
        return invokeMethod( obj, methodName, (Class<?>) null, null );
    }

    /**
     * 获取变量（不限于public, protected, private）
     * @param obj               当前调用的对象/类
     * @param fieldName         变量名
     * @return                  {@link Field}
     */
    public static Field getField(Object obj, String fieldName) {
        if( obj == null || TextUtils.isEmpty( fieldName ) ) return null;
        Class<?> cls = obj instanceof Class<?> ? (Class<?>) obj : obj.getClass();
        try {
            return cls.getField( fieldName );
        }catch(NoSuchFieldException e) {
            try {
                return cls.getDeclaredField( fieldName );
            }catch(NoSuchFieldException e1) {
                if( ( cls = cls.getSuperclass() ) == null ) {
                    LogUtil.e( "UtilsTAG", "Field [%s] not found.", fieldName );
                    return null;
                }
                return getField( cls, fieldName );
            }
        }
    }

    /**
     * 获取变量值
     * @param obj               当前调用的对象/类
     * @param fieldName         变量名
     * @return                  变量值
     */
    public static Object getFieldValue(Object obj, Object get, String fieldName) {
        try {
            Field field = getField( obj, fieldName );
            if( field == null ) return null;
            field.setAccessible( true );
            Object ret;
            try {
                ret = field.get( get );
            } catch (Exception e) {
                ret = field.get( null );
            }
            field.setAccessible( false );
            return ret;
        }catch(IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取变量值
     * @param obj               当前调用的对象/类
     * @param fieldName         变量名
     * @return                  变量值
     */
    public static Object getFieldValue(Object obj, String fieldName) {
        return getFieldValue( obj, obj, fieldName );
    }

    /**
     * 设置变量值
     * @param obj               当前调用的对象/类
     * @param fieldName         变量名
     * @param val               变量值
     * @return                  设置结果
     */
    public static boolean setFieldValue(Object obj, Object set, String fieldName, Object val) {
        try {
            Field field = getField( obj, fieldName );
            if( field == null ) return false;
            field.setAccessible( true );
            field.set( set, val );
            field.setAccessible( false );
            return true;
        }catch(IllegalAccessException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 设置变量值
     * @param obj               当前调用的对象/类
     * @param fieldName         变量名
     * @param val               变量值
     * @return                  设置结果
     */
    public static boolean setFieldValue(Object obj, String fieldName, Object val) {
        return setFieldValue( obj, obj, fieldName, val );
    }
}
